package network;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;
import java.nio.channels.SocketChannel;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;


public class SendData {
	private Map<SocketChannel, ByteBuffer> map;

	// used in the server
	public SendData() {
		map = Collections
				.synchronizedMap(new HashMap<SocketChannel, ByteBuffer>());
		// this.players = players;
	}

	private ByteBuffer objectToByteBuffer(String data) throws IOException {
		ByteArrayOutputStream bos = new ByteArrayOutputStream();
		ObjectOutput out = new ObjectOutputStream(bos);
		out.writeObject(data);
		byte[] res = bos.toByteArray();
		out.close();
		bos.close();
		return ByteBuffer.wrap(res);
	}

	// add data to the send-buffer, will start to send from the send-buffer when
	// the key.isReadable() say so
	/**
	 * Add data to the send-buffer, will start to send from the send-buffer when
	 * the connection is ready for it
	 */
	public boolean send(SocketChannel socket, String data, int packageID) {
		synchronized (map) {
			
			//doesnt allow null-data to be sent
			if(data == null)
				return false;

			if (!map.containsKey(socket))
				map.put(socket, null);

			ByteBuffer curHolder = map.get(socket);
			ByteBuffer newData = null;

			// if the conversion failed abort and return false
			try {
				newData = objectToByteBuffer(data);
			} catch (IOException e) {
				e.printStackTrace();
				map.remove(socket);
				return false;
			}
			
			// add a package-header to the new data
			PackageHeader header = new PackageHeader(newData.capacity(),
					packageID);

			// if the buffer wasnt empty, append new data-package
			if (curHolder != null) {
				ByteBuffer buf = ByteBuffer.allocate(curHolder.capacity()
						+ newData.capacity() + PackageHeader.HEADER_LENGTH);
				curHolder.position(0);
				buf.put(curHolder);
				buf.put(header.encodeHeader());
				buf.put(newData);
				curHolder = buf;
			}
			// the buffer was empty, add data
			else {
				curHolder = ByteBuffer.allocate(newData.capacity()
						+ PackageHeader.HEADER_LENGTH);
				curHolder.put(header.encodeHeader());
				curHolder.put(newData);
				// System.out.println("new message");
			}

			curHolder.position(0);
			map.put(socket, curHolder);
			return true;
		}
	}

	/**
	 * Send data from the send-buffer. The socket must be ready to send(!)
	 * shall be checked with isReadable() before. Used in Server.java
	 */
	public boolean send(SocketChannel socket) throws IOException {
		synchronized (map) {
			// SocketChannel socket = (SocketChannel) key.channel();

			// nothing to do
			if (!map.containsKey(socket) || socket == null)
				return true;
			// map.put(key, new Holder());

			ByteBuffer curHolder = map.get(socket);
			
			//if(curHolder == null)
			//	return false;
			

			// check if something went wrong with the socket
			try {
				int n = socket.write(curHolder);
				curHolder.position(n);
				// System.out.println(n);
			} catch (Exception e) {
				e.printStackTrace();
				return false;
			}
			curHolder = curHolder.slice();
			if (curHolder.remaining() == 0)
				map.remove(socket);

			// everything seems okay
			return true;
		}
	}
}
